%{
	#include "Expression.h"
	#include "Statement.h"
	#include "CastExpression.h"
	#include "CompoundStatement.h"
	#include "Declaration.h"
	#include "Do.h"
	#include "Else.h"
	#include "ExpressionStatement.h"
	#include "For.h"
	#include "Identifier.h"
	#include "If.h"
	#include "IterationStatement.h"
	#include "SelectionStatement.h"
	#include "UnaryExpression.h"
	#include "BinaryExpression.h"
	#include "While.h"
	#include <iostream>
	#include <vector>
	
	extern int yylex();
	void yyerror(const char *s){
		std::cout << "ERROR: "<< s << std::endl;
	}	
	CompoundStatement *program;
%}

%union {
	Expression *expression;
	Statement *statement;
	Identifier *identifier;
	Declaration *declaration;
	ExpressionStatement *expressionStatement;
	CompoundStatement *compoundStatement;
	std::vector<Identifier*> *identifierList;
	std::vector<Declaration*> *declarationList;
	std::vector<Statement*> *statementList;
	std::string *string;
	UnaryOperators uop;
	BinaryOperators bop;	
	int token;
	char ident;
}

%token<string> IDENTIFIER CONSTANT STRING_LITERAL
%token<token> CHAR SHORT INT LONG FLOAT DOUBLE SIZEOF MAIN ADD_ASSIGN SUB_ASSIGN MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN INC_OP DEC_OP AND_OP OR_OP LE_OP GE_OP EQ_OP NE_OP
%token<token> IF ELSE WHILE DO FOR

%type<statement> statement compound_statement iteration_statement selection_statement
%type<expressionStatement> expression_statement 
%type<expression> expression primary_expression postfix_expression unary_expression cast_expression 
%type<expression> relational_expression equality_expression logical_and_expression logical_or_expression 
%type<expression> multiplicative_expression additive_expression assignment_expression 
%type<identifier> init_declaration 
%type<declaration> declaration 
%type<ident> type_specifier
%type<bop> assignment_operator 
%type<uop> unary_operator
%type<declarationList> declaration_list
%type<statementList> statement_list
%type<identifierList> init_declaration_list

/*Operator precedence for matematical operators */
%left '+' '-'
%left '*' '/' '%'
%left '<' '>'
%error-verbose
%start start_program

%%
primary_expression
        : IDENTIFIER			{ 
        							$$ = new Identifier($1);
        						}
        | CONSTANT				{ 
        							$$ = new Expression();
        							$$->value = *$1;
        							$$->kind = Expression::constantExp; 
        						}
        | STRING_LITERAL		{
                					$$ = new Expression(); 
        							$$->value = *$1;
        							$$->kind = Expression::strExp; 
        						}
        | '(' expression ')'	{ $$ = $2; }
        ;
postfix_expression
        : primary_expression	{ $$ = $1; }
        | postfix_expression INC_OP	{ $$ = new UnaryExpression($1, PLUSPLUS, 1); }
        | postfix_expression DEC_OP	{ $$ = new UnaryExpression($1, MINUSMINUS, 1); }
        ;

unary_expression
        : postfix_expression	{ $$ = $1; } 
        | INC_OP unary_expression	{ $$ = new UnaryExpression($2, PLUSPLUS); }
        | DEC_OP unary_expression	{ $$ = new UnaryExpression($2, MINUSMINUS); }
        | unary_operator cast_expression { $$ = new UnaryExpression($2, $1); }
        | SIZEOF unary_expression	{ $$ = new UnaryExpression($2, SZEOF); }
        | SIZEOF '(' type_specifier ')'	{ 
        		Expression *exp;
				exp->kind = Expression::constantExp;
        		exp->value = $3;
        		$$ = new UnaryExpression(exp, SZEOF); 
        		}
        ;

unary_operator
        : '+'	{ $$ = UPLUS; }
        | '-'	{ $$ = UMINUS; }
        | '~'	{ $$ = BITNOT; }
        | '!'	{ $$ = UNEGAT; }
        ;

cast_expression
        : unary_expression	{ $$ = $1; }
        | '(' type_specifier ')' cast_expression	{ $$ = new CastExpression($2, $4); }
        ;

multiplicative_expression
        : cast_expression	{ $$ = $1; }
        | multiplicative_expression '*' cast_expression { $$ = new BinaryExpression( $1, MULTOP, $3); }
        | multiplicative_expression '/' cast_expression { $$ = new BinaryExpression( $1, DIVOP, $3); }
        | multiplicative_expression '%' cast_expression { $$ = new BinaryExpression( $1, REMOP, $3); }
        ;

additive_expression
        : multiplicative_expression	{ $$ = $1; }
        | additive_expression '+' multiplicative_expression { $$ = new BinaryExpression( $1, PLUSOP, $3); }
        | additive_expression '-' multiplicative_expression { $$ = new BinaryExpression( $1, MINOP, $3); }
        ;

relational_expression
        : additive_expression	{ $$ = $1; }
        | relational_expression '<' additive_expression { $$ = new BinaryExpression( $1, LTOP, $3); }
        | relational_expression '>' additive_expression { $$ = new BinaryExpression( $1, GTOP, $3); }
        | relational_expression LE_OP additive_expression { $$ = new BinaryExpression( $1, LEOP, $3); }
        | relational_expression GE_OP additive_expression { $$ = new BinaryExpression( $1, GEOP, $3); }
        ;

equality_expression
        : relational_expression	{ $$ = $1; }
        | equality_expression EQ_OP relational_expression { $$ = new BinaryExpression( $1, EQOP, $3); }
        | equality_expression NE_OP relational_expression { $$ = new BinaryExpression( $1, NEOP, $3); }
        ;

logical_and_expression
        : equality_expression	{ $$ = $1; }
        | logical_and_expression AND_OP equality_expression { $$ = new BinaryExpression( $1, ANDOP, $3); }
        ;

logical_or_expression
        : logical_and_expression	{ $$ = $1; }
        | logical_or_expression OR_OP logical_and_expression { $$ = new BinaryExpression( $1, OROP, $3); }
        ;

assignment_expression
        : logical_or_expression	{ $$ = $1; }
        | unary_expression assignment_operator assignment_expression  { $$ = new BinaryExpression( $1, $2, $3); }
        ;

assignment_operator
        : '='	{ $$ = SIASSIG; }
        | MUL_ASSIGN { $$ = MUASSIG; }
        | DIV_ASSIGN { $$ = DIASSIG; }
        | MOD_ASSIGN { $$ = REASSIG; }
        | ADD_ASSIGN { $$ = PLASSIG; }
        | SUB_ASSIGN { $$ = MIASSIG; }
        ;

expression
        : assignment_expression	{ $$ = $1; }
        | expression ',' assignment_expression { $$ = $3; }
        ;

declaration
        : type_specifier init_declaration_list ';' { $$ = new Declaration($1, $2); }
        ;

init_declaration_list
        : init_declaration	{
								$$ = new std::vector<Identifier*>();
								$$->push_back($1); 
							}
        | init_declaration_list ',' init_declaration	{ $1->push_back($3); }
        ;

init_declaration
        : IDENTIFIER	{ $$ = new Identifier($1); }
        | IDENTIFIER '=' assignment_expression	{ $$ = new Identifier($1, $3); }
        ;

type_specifier
        : CHAR 		{$$ = 'C';}
        | SHORT		{$$ = 'S';}
        | INT		{$$ = 'I';}
        | LONG		{$$ = 'L';}
        | FLOAT		{$$ = 'F';}
        | DOUBLE	{$$ = 'D';}
        ;

statement
        : compound_statement {$$ = $1;}
        | expression_statement { $$ = $1;}
        | selection_statement { $$ = $1; }
        | iteration_statement { $$ = $1; }
        ;


compound_statement
        : '{' '}' 	{ $$ = new CompoundStatement(); }
        | '{' statement_list '}'	{ $$ = new CompoundStatement($2); }
        | '{' declaration_list '}'	{ $$ = new CompoundStatement($2); }
        | '{' declaration_list statement_list '}' { $$ = new CompoundStatement($2, $3); }
        ;

declaration_list
        : declaration { 
						$$ = new std::vector<Declaration*>();
						$$->push_back($1); 
						}
        | declaration_list declaration { $1->push_back($2); }
        ;

statement_list
        : statement  { 
						$$ = new std::vector<Statement*>();
						$$->push_back($1); 
					}
        | statement_list statement { $1->push_back($2); }
        ;

expression_statement
        : ';'	{ $$ = new ExpressionStatement(); }
        | expression ';'	{ $$ = new ExpressionStatement($1); }
        ;

selection_statement
        : IF '(' expression ')' statement	{ $$ = new If($3, $5); }
        | IF '(' expression ')' statement ELSE statement	{ Else *temp = new Else($7); $$ = new If($3,$5, temp);}
        ;

iteration_statement
        : WHILE '(' expression ')' statement { $$ = new While($3, $5); }
        | DO statement WHILE '(' expression ')' ';' { $$ = new Do($5,$2); }
        | FOR '(' expression_statement expression_statement ')' statement { $$ = new For($3,$4,$6); }
        | FOR '(' expression_statement expression_statement expression ')' statement { $$ = new For($3,$4,$5,$7); }
        ;

start_program
        : MAIN  compound_statement { program = $<compoundStatement>2; }
        ;

%%
